#include "ATMOS_NetworkManager.h"
#include "ATMOS_Packet.h"
#include "ATMOS_PacketListener.h"
#include "ATMOS_NetworkConnectionInfo.h"

#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"

#include <sstream>
#include <iostream>
#include <algorithm>

#define RAKNET_CHAR_STR_BUFFER_SIZE 1000
#define WRITE_FUNCTION_NAME() std::cout << __FUNCTION__ << std::endl;

using namespace ATMOS;

// NetworkManager
////////////////////////////////////////////////////////////////////////////////
const RakNetPacketID NetworkManager::sAtmosCustomPacketID = ID_USER_PACKET_ENUM;

NetworkManager::NetworkManager() :
mPeerInterface( 0 ),
mStatistics( 0 ),
mShutdownTime( 0 )
{
}

NetworkManager::~NetworkManager()
{
}

void NetworkManager::initialize( const NetworkConnectionInfo &connectionInfo )
{
	mShutdownTime = connectionInfo.shutdownTime;

	ATMOS_LOG("server IP: " << connectionInfo.serverIP << std::endl
		<< "server port: " << connectionInfo.serverPort << std::endl
		<< "client port: " << connectionInfo.clientPort << std::endl
		<< "max connections: " << connectionInfo.maxConnections << std::endl
		<< "occasional ping: " << connectionInfo.occasionalPing << std::endl
		<< "unreliable timeout: " << connectionInfo.unreliableTimeout << std::endl
		<< "shutdown time: " << connectionInfo.shutdownTime << std::endl);
}

void NetworkManager::update()
{
	RakNet::Packet *packet = 0;
	packet = mPeerInterface->Receive();
	while( packet )
	{
		handleRakNetPacket( packet );

		mPeerInterface->DeallocatePacket( packet );
		packet = mPeerInterface->Receive();
	}
}

void NetworkManager::shutdown()
{
	ATMOS_LOG_FUNC();
	mPeerInterface->Shutdown( mShutdownTime );
}

const char *rakNetIDToString( RakNetPacketID id )
{
	const char *ret = 0;
	switch( id )
	{
	case NetworkManager::sAtmosCustomPacketID: ret = "ATMOS packet"; break;
	case ID_DISCONNECTION_NOTIFICATION: ret = "ID_DISCONNECTION_NOTIFICATION"; break;
	case ID_ALREADY_CONNECTED: ret = "ID_ALREADY_CONNECTED"; break;
	case ID_INCOMPATIBLE_PROTOCOL_VERSION: ret = "ID_INCOMPATIBLE_PROTOCOL_VERSION"; break;
	case ID_REMOTE_DISCONNECTION_NOTIFICATION: ret = "ID_REMOTE_DISCONNECTION_NOTIFICATION"; break;
	case ID_NEW_INCOMING_CONNECTION: ret = "ID_NEW_INCOMING_CONNECTION"; break;
	case ID_CONNECTION_BANNED: ret = "ID_CONNECTION_BANNED"; break;
	case ID_CONNECTION_ATTEMPT_FAILED: ret = "ID_CONNECTION_ATTEMPT_FAILED"; break;
	case ID_NO_FREE_INCOMING_CONNECTIONS: ret = "ID_NO_FREE_INCOMING_CONNECTIONS"; break;
	case ID_INVALID_PASSWORD: ret = "ID_INVALID_PASSWORD"; break;
	case ID_CONNECTION_LOST: ret = "ID_CONNECTION_LOST"; break;
	case ID_CONNECTION_REQUEST_ACCEPTED: ret = "ID_CONNECTION_REQUEST_ACCEPTED"; break;
	default: ret = "UNKNOWN PACKET TYPE"; break;
	}
	ATMOS_ASSERT(ret);
	return ret;
}

void NetworkManager::handleRakNetPacket( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();

	std::string strIP = packet->systemAddress.ToString( false );
	ATMOS_LOG("PACKET ORIGIN IP: "<< strIP);

	RakNetPacketID id = getRakNetPacketID( packet );
	ATMOS_LOG( rakNetIDToString( id ) );
	switch( id )
	{
	case sAtmosCustomPacketID: onAtmosPacket( packet );	break;
	case ID_DISCONNECTION_NOTIFICATION: onDisconnectionNotification( packet ); break;
	case ID_ALREADY_CONNECTED: onAlreadyConnected( packet ); break;
	case ID_INCOMPATIBLE_PROTOCOL_VERSION: onIncompatibleProtocolVersion( packet ); break;
	case ID_REMOTE_DISCONNECTION_NOTIFICATION: onRemoteDisconnectionNotification( packet ); break;
	case ID_NEW_INCOMING_CONNECTION: onNewIncomingConnection( packet ); break;
	case ID_CONNECTION_BANNED: onConnectionBanned( packet ); break;
	case ID_CONNECTION_ATTEMPT_FAILED: onConnectionAttemptFailed( packet ); break;
	case ID_NO_FREE_INCOMING_CONNECTIONS: onNoFreeIncomingConnections( packet ); break;
	case ID_INVALID_PASSWORD: onInvalidPassword( packet ); break;
	case ID_CONNECTION_LOST: onConnectionLost( packet ); break;
	case ID_CONNECTION_REQUEST_ACCEPTED: onConnectionRequestAccepted( packet ); break;
	default:
		onUnknownPacket( packet ); break;
		break;
	}
}

RakNetPacketID NetworkManager::getRakNetPacketID( RakNet::Packet *packet )
{
	if (packet==0)
		return 255;

	if ((unsigned char)packet->data[0] == ID_TIMESTAMP)
	{
		RakAssert(packet->length > sizeof(RakNet::MessageID) + sizeof(RakNet::Time));
		return (RakNetPacketID)packet->data[sizeof(RakNet::MessageID) + sizeof(RakNet::Time)];
	}
	else
		return (RakNetPacketID)packet->data[0];
}

void NetworkManager::onAtmosPacket( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
 	ATMOS_LOG("packet ID: " << Packet( packet->data, packet->bitSize ).getAtmosPacketID());

	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		Packet p( packet->data, packet->bitSize );
		(*iter)->onAtmosPacket( &p );
	}
}

void NetworkManager::onDisconnectionNotification( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onDisconnectionNotification( packet->guid.ToString() );
	}
}

void NetworkManager::onAlreadyConnected( RakNet::Packet *packet )
{
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onAlreadyConnected( packet->guid.ToString() );
	}
}

void NetworkManager::onIncompatibleProtocolVersion( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onIncompatibleProtocolVersion( packet->guid.ToString() );
	}
}

void NetworkManager::onRemoteDisconnectionNotification( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onRemoteDisconnectionNotification( packet->guid.ToString() );
	}
}

void NetworkManager::onRemoteConnectionLost( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onRemoteConnectionLost( packet->guid.ToString() );
	}
}

void NetworkManager::onNewIncomingConnection( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onNewIncomingConnection( packet->guid.ToString() );
	}
}

void NetworkManager::onConnectionBanned( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onConnectionBanned( packet->guid.ToString() );
	}
}

void NetworkManager::onConnectionAttemptFailed( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onConnectionAttemptFailed( packet->guid.ToString() );
	}
}

void NetworkManager::onNoFreeIncomingConnections( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onNoFreeIncomingConnections( packet->guid.ToString() );
	}
}

void NetworkManager::onInvalidPassword( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onInvalidPassword( packet->guid.ToString() );
	}
}

void NetworkManager::onConnectionLost( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onConnectionLost( packet->guid.ToString() );
	}
}

void NetworkManager::onConnectionRequestAccepted( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onConnectionRequestAccepted( packet->guid.ToString() );
	}
}

void NetworkManager::onUnknownPacket( RakNet::Packet *packet )
{
	ATMOS_LOG_FUNC();
	for( PacketListenerVector::iterator iter = mPacketListeners.begin();
		iter != mPacketListeners.end();
		++iter )
	{
		(*iter)->onUnknownPacket( packet->guid.ToString() );
	}
}

void NetworkManager::addPacketListener( PacketListener *listener )
{
	PacketListenerVector::iterator iter =
		std::find( mPacketListeners.begin(), mPacketListeners.end(), listener );
	if( iter != mPacketListeners.end() )
	{
		ATMOS_THROW_LOGIC( "packet listener already registered" );
	}

	mPacketListeners.push_back( listener );
}

void NetworkManager::removePacketListener( PacketListener *listener )
{
	PacketListenerVector::iterator iter =
		std::find( mPacketListeners.begin(), mPacketListeners.end(), listener );
	if( iter != mPacketListeners.end() )
	{
		mPacketListeners.erase( iter );
	}
	else
	{
		ATMOS_THROW_LOGIC("tried to remove a packet listener that isn't registered");
	}
}

void NetworkManager::sendPacket( Packet *packet, const std::string &guid, 
	ATMOS::PacketPriority priority, ATMOS::PacketReliability reliability )
{
	ATMOS_ASSERT(packet);
	RakNet::RakNetGUID _guid;
	_guid.FromString( guid.c_str() );

	mPeerInterface->Send(
		packet->getBitStream(),
		(::PacketPriority)priority,
		(::PacketReliability)reliability,
		0,
		mPeerInterface->GetSystemAddressFromGuid( _guid ),
		false
		);
}

void NetworkManager::broadcastPacket( Packet *packet, ATMOS::PacketPriority priority, 
	ATMOS::PacketReliability reliability )
{
	ATMOS_ASSERT(packet);
	RakNet::RakNetGUID _guid;
	_guid.FromString( packet->getRakNetGUID().c_str() );

	mPeerInterface->Send(
		packet->getBitStream(),
		(::PacketPriority)priority,
		(::PacketReliability)reliability,
		0,
		RakNet::UNASSIGNED_SYSTEM_ADDRESS,
		true
		);
}

void NetworkManager::broadcastPacketToAllBut( Packet *packet, const std::string &nonRecipientGUID, 
	ATMOS::PacketPriority priority, ATMOS::PacketReliability reliability )
{
	ATMOS_ASSERT(packet);
	RakNet::RakNetGUID _guid;
	_guid.FromString( nonRecipientGUID.c_str() );

	mPeerInterface->Send(
		packet->getBitStream(),
		(::PacketPriority)priority,
		(::PacketReliability)reliability,
		0,
		mPeerInterface->GetSystemAddressFromGuid( _guid ),
		true
		);
}

RakNet::RakPeerInterface *NetworkManager::getRakNetPeerInterface()
{
	ATMOS_ASSERT(mPeerInterface);
	return mPeerInterface;
}

std::string NetworkManager::getLocalGUID() const
{
	ATMOS_ASSERT(mPeerInterface);
	return mPeerInterface->GetMyGUID().ToString();
}